//打折促销
package shopping

import (
    //"fmt"
    "time"
	"errors"
	"strings"
	"strconv"
	"dhfshop/models"
	"dhfshop/models/member"
	"dhfshop/models/consts"
	"dhfshop/utils/math"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
)

// int会被orm映射成11位，需要用int64

// 打折活动表
type Activity struct {
    ActivityId   int64     `orm:"column(activity_id);pk;auto" json:"activity_id" form:"activity_id"` //活动ID
    PartnerId    int64       `orm:"column(partner_id)" json:"partner_id" form:"partner_id"`            //商家ID
	StoreId      int64       `orm:"column(store_id)" json:"store_id" form:"store_id"`                  //门店ID
	Name         string    `orm:"column(name);size(32);null" json:"name" form:"name"`       //活动名称(最多5个汉字或者5个数字字母) 
	Ptype        int       `orm:"column(ptype)" json:"ptype" form:"ptype"`                  //活动范围 1=全店, 2=部分商品
	StartTime    time.Time `orm:"column(start_time);type(datetime);null" json:"-" form:"-"` //活动开始时间
	StartTimeStr string    `orm:"-" json:"start_time" form:"start_time"`          //输入输出
	EndTime      time.Time `orm:"column(end_time);type(datetime);null" json:"-" form:"-"`   //活动结束时间
	EndTimeStr   string    `orm:"-" json:"end_time" form:"end_time"`              //输入输出
	TypeId       int       `orm:"column(type_id)" json:"type_id" form:"type_id"`            //折扣方式 1=打折,2=减钱
    Discount     float32   `orm:"column(discount);digits(12);decimals(2)" json:"discount" form:"discount"`   //打几折
    Disprice     float32   `orm:"column(disprice);digits(12);decimals(2)" json:"disprice" form:"disprice"`   //减多少钱
    Level        int       `orm:"column(level);default(0)" json:"level" form:"level"`       //会员等级
	ActivityItems []*ActivityItem `orm:"reverse(many)" json:"activity_items" form:"-"`      /* 反向关系,用来表述ORM存储关系和组合JSON输出*/
    ActivityItemsJson string  `orm:"-" json:"-" form:"activity_items"`                      /* 临时存放上传的订单sku*/	    
	CreatedAt     time.Time   `orm:"auto_now_add;column(created_at);type(datetime)" json:"-" form:"-"` 
	CreatedAtStr  string      `orm:"-" json:"created_at" form:"-"`
    UpdatedAt     time.Time   `orm:"auto_now;column(updated_at);type(datetime)" json:"-" form:"-"`         
	UpdatedAtStr  string      `orm:"-" json:"updated_at" form:"-"`
}


//参加活动商品表
type ActivityItem struct {
    Id         int64     `orm:"column(id);pk;auto" json:"id" form:"id"`
	PartnerId  int64       `orm:"column(partner_id)" json:"partner_id" form:"partner_id"`   //商家ID
	Activity   *Activity `orm:"column(activity_id);rel(fk);on_delete(cascade)" json:"-" ` //
    ActivityId int64     `orm:"-" json:"activity_id" form:"activity_id"`
	ItemId     int64     `orm:"column(item_id);null" json:"item_id,string" form:"-"`      //商品ID
	ItemIdStr  string    `orm:"-" json:"-" form:"item_id"`   //输入
	SkuId      int64     `orm:"column(sku_id);null" json:"sku_id,string" form:"-"`     //规格ID
    SkuIdStr   string    `orm:"-" json:"-" form:"sku_id"`   //输入
	TypeId     int       `orm:"column(type_id)" json:"type_id" form:"type_id"`     //折扣方式 1=打折,2=减钱
	Discount   float32   `orm:"column(discount);digits(12);decimals(2)" json:"discount" form:"discount"`  //打几折
    Disprice   float32   `orm:"column(disprice);digits(12);decimals(2)" json:"disprice" form:"disprice"`  //减多少钱
}

func (u *ActivityItem) TableUnique() [][]string {
	return [][]string{
		[]string{"ItemId", "SkuId"},
	}
}

func init() {
    orm.RegisterModel(new(Activity),new(ActivityItem))   
}

func AddActivity(m *Activity) (id int64, err error) {
	o := models.GetDefaultDB()
	id, err = o.Insert(m)
	return
}

func AddActivityItem(m *ActivityItem) (id int64, err error) {
	o := models.GetDefaultDB()
	id, err = o.Insert(m)
	return
}

func AddMultiActivityItems(list []*ActivityItem) (err error) {
	size := len(list)
	o := models.GetDefaultDB()
	if size > 1 {
	    _, err = o.InsertMulti(size, list) 
	}else{
	   _, err = o.Insert(list[0])
	}
	return
}

func GetActivityById(id int64) (v *Activity, err error) {
	o := models.GetDefaultDB()
	v = &Activity{ActivityId: id}
	if err = o.Read(v); err == nil {
		/*
		o.LoadRelated(v, "ActivityItems")
		for _, item := range v.ActivityItems {
		    item.ActivityId = v.ActivityId
			item.ItemIdStr = strconv.FormatInt(item.ItemId, 10)
            item.SkuIdStr = strconv.FormatInt(item.SkuId, 10)
			item.Activity = nil
		}
		*/
		v.StartTimeStr = v.StartTime.Format(consts.DATETIME_FORMAT)
		v.EndTimeStr = v.EndTime.Format(consts.DATETIME_FORMAT)
		return v, nil
	}
	return nil, err
}

func GetActivityIdArrBySkuId(itemId, skuId int64) (map[int64]int32,error) {
	items := []*ActivityItem{}
	actIdsMap := map[int64]int32{}
	o := models.GetDefaultDB()
	qs := o.QueryTable(new(ActivityItem))
	if itemId > 0 {
	   qs = qs.Filter("item_id",itemId)
	}
	if skuId > 0 {
	   qs = qs.Filter("sku_id",skuId)
	}
	if _, err := qs.RelatedSel("Activity").All(&items); err != nil {
	    return actIdsMap,err
	}
	for _, item := range items {
		actIdsMap[item.Activity.ActivityId] = 1
	}
	return actIdsMap, nil
}

func GetActivitysByStoreId(partnerId, storeId int64) (activitys []*Activity, err error) {
	activitys = []*Activity{}
	o := models.GetDefaultDB()
	qs := o.QueryTable(new(Activity))
	if partnerId > 0 {
	   qs = qs.Filter("partner_id",partnerId)
	}
	if storeId > 0 {
	   qs = qs.Filter("store_id",storeId)
	}
	_, err = qs.All(&activitys)
	return
}

func GetAllActivitys(query map[string]string,fields []string,order_by map[string]string,offset,limit int)([]*Activity,int64,error) {
	o := models.GetDefaultDB()
	qs := o.QueryTable(new(Activity))
	for k, v := range query {
		k = strings.Replace(k, ".", "__", -1)
		qs = qs.Filter(k, v)
	}
	cnt, err := qs.Count()
	// order by:
	var sortFields []string
	for k, v := range order_by {
		orderby := ""
		if v == "desc" {
			orderby = "-" + k
		} else if v == "asc" {
			orderby = k
		} else {
			return nil, 0, errors.New("Error: Invalid order. Must be either [asc|desc]")
		}
		sortFields = append(sortFields, orderby)
	}
	qs = qs.OrderBy(sortFields...)
	var lists []*Activity
	if _, err := qs.Limit(limit, offset).All(&lists, fields...); err == nil {
	    for _, temp := range lists {
			temp.StartTimeStr = temp.StartTime.Format(consts.DATETIME_FORMAT)
			temp.EndTimeStr = temp.EndTime.Format(consts.DATETIME_FORMAT)
			o.LoadRelated(temp, "ActivityItems")
			for _, item := range temp.ActivityItems { 
				item.ActivityId = item.Activity.ActivityId			
			    item.Activity = nil
			}
	    }
		return lists, cnt, nil
	}
	return nil, 0, err
}

func GetActivityItemById(id int64) (v *ActivityItem, err error) {
	o := models.GetDefaultDB()
	v = &ActivityItem{Id: id}
	if err = o.Read(v); err == nil {
		return v, nil
	}
	return nil, err
}

func GetActivityItem(activityId, itemId, skuId int64) (item *ActivityItem, err error) {
	item = &ActivityItem{}
	o := models.GetDefaultDB()
	qs := o.QueryTable(new(ActivityItem))
	if activityId > 0 {
	   qs = qs.Filter("activity_id", activityId)
	}
	if itemId > 0 {
	   qs = qs.Filter("item_id", itemId)
	}
	skuIds :=[]string{"0"}
	if skuId > 0 {
	   skuIds = append(skuIds, strconv.FormatInt(skuId, 10))
	   qs = qs.Filter("sku_id__in", skuIds)
	}
	err = qs.One(item)
	return
}

func GetAllActivityItems(query map[string]string,fields []string,order_by map[string]string,offset,limit int)([]*ActivityItem,int64,error) {
	o := models.GetDefaultDB()
	qs := o.QueryTable(new(ActivityItem))
	for k, v := range query {
		k = strings.Replace(k, ".", "__", -1)
		//beego.Debug("GetAllActivityItems Query:", k, v)
		if ok := strings.Contains(v, "|"); ok {    //in order to: in
		    //beego.Debug("In Query:", v)
			valueArr := strings.Split(v, "|")
			qs = qs.Filter(k, valueArr)
		}else{
		    qs = qs.Filter(k, v)
		}
	}
	cnt, err := qs.Count()
	// order by:
	var sortFields []string
	for k, v := range order_by {
		orderby := ""
		if v == "desc" {
			orderby = "-" + k
		} else if v == "asc" {
			orderby = k
		} else {
			return nil, 0, errors.New("Error: Invalid order. Must be either [asc|desc]")
		}
		sortFields = append(sortFields, orderby)
	}
	qs = qs.OrderBy(sortFields...)
	var lists []*ActivityItem
	if _, err := qs.Limit(limit, offset).All(&lists, fields...); err == nil {
	    for _, temp := range lists {
			temp.ActivityId = temp.Activity.ActivityId
	    }
		return lists, cnt, nil
	}
	return nil, 0, err
}

func UpdateActivityById(m *Activity, k ...string) (err error) {
	o := models.GetDefaultDB()
	v := Activity{ActivityId: m.ActivityId}
	if err = o.Read(&v); err != nil {
		return err
	}else{
	    if _, err = o.Update(m, k...); err != nil {
		   return err
		}
	}
	return nil
}

func UpdateActivityItem(m *ActivityItem, k ...string) (err error) {
    o := models.GetDefaultDB()
	v := ActivityItem{Id: m.Id}
	if err = o.Read(&v); err == nil {
		_, err = o.Update(m, k...)
		return err
	}
	return
}

func DeleteActivity(id int64) (err error) {
	o := models.GetDefaultDB()
	v := Activity{ActivityId: id}
	if err = o.Read(&v); err == nil {
		if _, err = o.Delete(&v); err == nil {
			return nil
		}
	}
	return
}

func DeleteActivityItem(id int64) (err error) {
	o := models.GetDefaultDB()
	v := ActivityItem{Id: id}
	if err = o.Read(&v); err == nil {
		if _, err = o.Delete(&v); err == nil {
			return nil
		}
	}
	return
}

/***************************************************************************
根据activity_id把活动查询出来，判断活动类型：这3个里面选择1个出来进行优惠
1. 店铺级别优惠活动
2. 商品级别优惠会活动
3. sku级别优惠活动

因为有所约定：在保存优惠活动的时候，一个商品如果存在商品级别优惠，就不能存在规格级别优惠。
如果存在了规格级别优惠，就不能有商品级别的优惠。

淘宝也是这样的：前端
商品 后面 会列出来  优惠活动，如果是多个的话，可以选择一个
********************************************************************************/
func GetDiscountFee(itemId, skuId, activityId int64, price float32, memberId int64) (float32, error){
	var discountFee float32 = 0
	activity, err := GetActivityById(activityId)
	if err != nil {
	    return 0, errors.New("activity is null")
	}
	curTime := time.Now().UTC().Unix()
	activityStartTime := activity.StartTime.UTC().Unix()
	activityEndTime := activity.EndTime.UTC().Unix()
	if (curTime < activityStartTime)||(curTime >= activityEndTime) {
	    return 0, errors.New("activity time is invalid")
	}
	//活动会员等级校验
	//beego.Debug("GetDiscountFee memberId:", memberId, "activityId:", activityId)
	if memberId > 0 {
	    if memberVaue, err := member.GetMemberById(memberId); err == nil {
			//beego.Debug("activityId:", activityId, "activity.Level:", activity.Level, "memberVaue.Level:", memberVaue.Level)
			if (activity.Level != memberVaue.Level)&& (activity.Level != 0){
				return 0, errors.New("member level is not equal")
			}
		}
	}
	//全店校验
	if activity.Ptype == 1 {
	    if activity.TypeId == 1 {       //打折
		    afterDiscountPrice := math.MultiFloat32Price(price, math.DivTotalFee2(activity.Discount, 100))
			discountFee = math.SubPrice(price, afterDiscountPrice)
			//beego.Debug("activityId:", activityId, "discountfee1:", discountFee)
		}else if activity.TypeId == 2 {  //减钱
		    discountFee = activity.Disprice
			//beego.Debug("activityId:", activityId, "discountfee2:", discountFee)
		}else{
		
		}
		return discountFee, nil
	}
	// 由于互斥，因此商品级别和规格级别在一起查询出来一条就可以了
	activityItem, err := GetActivityItem(activityId,itemId,skuId)
	if activityItem.TypeId == 1 {       //打折
		afterDiscountPrice := math.MultiFloat32Price(price, math.DivTotalFee2(activityItem.Discount, 100))
		discountFee = math.SubPrice(price, afterDiscountPrice)
		//beego.Debug("activityId:", activityId, "discountfee3:", discountFee)
	}else if activityItem.TypeId == 2 {  //减钱
		discountFee = activityItem.Disprice
		//beego.Debug("activityId:", activityId, "discountfee4:", discountFee)
	}else{
	
	}
	//beego.Debug("activityId:", activityId, "discountFee:", discountFee)
	return discountFee, nil
}

func GetMaxDiscountFee(partnerId,storeId,itemId,skuId int64, level int,price float32) (int64, float32, error){
	var maxDiscountFee float32 = 0
	var curActivityId int64 = 0
	var validActivitys []*Activity
	activitys, err := GetActivitysByStoreId(partnerId, storeId)
	if err != nil {
	    beego.Debug("GetMaxDiscountFee error:", err.Error())
		return curActivityId,price, errors.New("activity is null")
	}
	beego.Debug("GetMaxDiscountFee itemId:", itemId)
	skuActList, _:= GetActivityIdArrBySkuId(itemId, skuId)
	if len(skuActList) < 1 {
	   skuActList, _= GetActivityIdArrBySkuId(itemId, 0)
	}
	curTime := time.Now().UTC().Unix()
	//
	for _, activity := range activitys {
	    activityStartTime := activity.StartTime.UTC().Unix()
		activityEndTime := activity.EndTime.UTC().Unix()
		if (curTime < activityStartTime)||(curTime >= activityEndTime) {
			beego.Debug("activity invalid time:", activity.ActivityId)
			continue
		}
	    //部分商品校验
	    if activity.Ptype == 2 {
			if _, ok := skuActList[activity.ActivityId]; !ok {
			    beego.Debug("activity invalid item:", activity.ActivityId)
				continue
            }			
		}
		if level > 0 {
		    if (level != activity.Level)&&(activity.Level != 0) {
			    beego.Debug("activity invalid level:", activity.ActivityId)
				continue
			}
		}
		validActivitys = append(validActivitys, activity)
	}
	if len(validActivitys) == 0 {
	   return curActivityId, price, errors.New("activity is null")
	}
	//
	for _, activity := range validActivitys {
	    beego.Debug("activity valid id:", activity.ActivityId)
		if activity.Ptype == 1 {   //全店优惠
		   if activity.TypeId == 1 {       //打折
				afterDiscountPrice := math.MultiFloat32Price(price, math.DivTotalFee2(activity.Discount, 100))
				discountFee := math.SubPrice(price, afterDiscountPrice)
				beego.Debug("discountFee:", discountFee, "maxDiscountFee:", maxDiscountFee)
				if discountFee > maxDiscountFee {
				   maxDiscountFee = discountFee
				   curActivityId = activity.ActivityId
				   beego.Debug("curActivityId:", curActivityId)
				}
			}else if activity.TypeId == 2 {  //减钱
				discountFee := activity.Disprice
				beego.Debug("discountFee:", discountFee, "maxDiscountFee:", maxDiscountFee)
				if discountFee > maxDiscountFee {
				   maxDiscountFee = discountFee
				   curActivityId = activity.ActivityId
				   beego.Debug("curActivityId:", curActivityId)
				}
			}else{
			
			}
		}else if activity.Ptype == 2 {
		    if activityItem, err := GetActivityItem(activity.ActivityId,itemId,skuId); err == nil {
			    if activityItem.TypeId == 1 {
					afterDiscountPrice := math.MultiFloat32Price(price, math.DivTotalFee2(activityItem.Discount, 100))
					discountFee := math.SubPrice(price, afterDiscountPrice)
					beego.Debug("itemId:",itemId,"price:",price,"discountrule:",activityItem.Discount,"afterDiscountPrice:",afterDiscountPrice,"discountFee:",discountFee,"maxDiscountFee:",maxDiscountFee)
					if discountFee > maxDiscountFee {
					   maxDiscountFee = discountFee
					   curActivityId = activity.ActivityId
					   beego.Debug("curActivityId:", curActivityId)
					}
				}else if activityItem.TypeId == 2 {
					discountFee := activityItem.Disprice
					beego.Debug("discountFee:", discountFee, "maxDiscountFee:", maxDiscountFee)
					if discountFee > maxDiscountFee {
					   maxDiscountFee = discountFee
					   curActivityId = activity.ActivityId
					   beego.Debug("curActivityId:", curActivityId)
					}
				}else{
				
				}
			}else{
			    beego.Debug("GetActivityItem err:", err.Error())
			}
		}else{
		
		}
	}
	promPrice := math.SubPrice(price, maxDiscountFee)
	beego.Debug("curActivityId:",curActivityId,"maxDiscountFee:",maxDiscountFee,"promPrice:",promPrice)
	return curActivityId, promPrice, nil
}

func GetMaxDiscountFeeModel2(partnerId,storeId,itemId,skuId int64, level int,price float32) (int64, float32, error){
	var maxDiscountFee float32 = 0
	var curActivityId int64 = 0
	var validActivitys []*Activity
	activitys, err := GetActivitysByStoreId(partnerId, storeId)
	if err != nil {
		beego.Debug("GetMaxDiscountFeeModel2 error:", err.Error())
		return curActivityId,price, errors.New("activity is null")
	}
	beego.Debug("GetMaxDiscountFeeModel2 itemId:", itemId)
	skuActList, _:= GetActivityIdArrBySkuId(itemId, skuId)
	if len(skuActList) < 1 {
	   skuActList, _= GetActivityIdArrBySkuId(itemId, 0)
	}
	curTime := time.Now().UTC().Unix()
	//
	for _, activity := range activitys {
	    activityStartTime := activity.StartTime.UTC().Unix()
		activityEndTime := activity.EndTime.UTC().Unix()
		if (curTime < activityStartTime)||(curTime >= activityEndTime) {
			beego.Debug("activity invalid time:", activity.ActivityId)
			continue
		}
	    //部分商品校验
	    if activity.Ptype == 2 {
			if _, ok := skuActList[activity.ActivityId]; !ok {
			    beego.Debug("activity invalid item:", activity.ActivityId)
				continue
            }			
		}
		if level > 0 {
		    if (level != activity.Level)&&(activity.Level != 0) {
			    beego.Debug("activity invalid level:", activity.ActivityId)
				continue
			}
		}else{
		    if activity.Level != 0 {
				beego.Debug("activity.Level != 0")
				continue
			}
		}
		validActivitys = append(validActivitys, activity)
	}
	if len(validActivitys) == 0 {
	   beego.Debug("activity is null")
	   return curActivityId, price, errors.New("activity is null")
	}
	//
	for _, activity := range validActivitys {
	    beego.Debug("activity valid id:", activity.ActivityId)
		if activity.Ptype == 1 {   //全店优惠
		   if activity.TypeId == 1 {       //打折
				afterDiscountPrice := math.MultiFloat32Price(price, math.DivTotalFee2(activity.Discount, 100))
				discountFee := math.SubPrice(price, afterDiscountPrice)
				beego.Debug("discountFee:", discountFee, "maxDiscountFee:", maxDiscountFee)
				if discountFee > maxDiscountFee {
				   maxDiscountFee = discountFee
				   curActivityId = activity.ActivityId
				   beego.Debug("curActivityId:", curActivityId)
				}
			}else if activity.TypeId == 2 {  //减钱
				discountFee := activity.Disprice
				beego.Debug("discountFee:", discountFee, "maxDiscountFee:", maxDiscountFee)
				if discountFee > maxDiscountFee {
				   maxDiscountFee = discountFee
				   curActivityId = activity.ActivityId
				   beego.Debug("curActivityId:", curActivityId)
				}
			}else{
			
			}
		}else if activity.Ptype == 2 {
		    beego.Debug("GetActivityItem activity_id:",activity.ActivityId,"itemId:",itemId,"skuId:", skuId)
			if activityItem, err := GetActivityItem(activity.ActivityId,itemId,skuId); err == nil {
			    if activityItem.TypeId == 1 {
					afterDiscountPrice := math.MultiFloat32Price(price, math.DivTotalFee2(activityItem.Discount, 100))
					beego.Debug("price:",price,"item discount:",activityItem.Discount,"afterDiscountPrice:", afterDiscountPrice)
					
					discountFee := math.SubPrice(price, afterDiscountPrice)
					beego.Debug("discountFee:", discountFee, "maxDiscountFee:", maxDiscountFee)
					if discountFee > maxDiscountFee {
					   maxDiscountFee = discountFee
					   curActivityId = activity.ActivityId
					   beego.Debug("curActivityId:", curActivityId)
					}
				}else if activityItem.TypeId == 2 {
					discountFee := activityItem.Disprice
					beego.Debug("discountFee:", discountFee, "maxDiscountFee:", maxDiscountFee)
					if discountFee > maxDiscountFee {
					   maxDiscountFee = discountFee
					   curActivityId = activity.ActivityId
					   beego.Debug("curActivityId:", curActivityId)
					}
				}else{
				    
				}
			}else{
			    beego.Debug("GetActivityItem err:", err.Error())
			}
		}else{
		
		}
	}
	promPrice := math.SubPrice(price, maxDiscountFee)
	beego.Debug("curActivityId:",curActivityId,"maxDiscountFee:",maxDiscountFee,"promPrice:",promPrice)
	return curActivityId, promPrice, nil
}
